local capabilities = vim.lsp.protocol.make_client_capabilities();
require('cmp_nvim_lsp').default_capabilities(capabilities);

-- wrap raw function inside a function call.
-- this is for avoid passing null into keymaps (when function get deprecated)
-- it's hard to debug
local fn = function(f)
	return function() f() end
end

local on_attach = function(client, bufnr)
	local navic = require("nvim-navic")
	-- if client.server_capabilities.document_symbol then
	navic.attach(client, bufnr)
	-- end
	local telescope = require "telescope.builtin"

	-- local function buf_set_option(...) vim.api.nvim_buf_set_option(bufnr, ...) end

	-- Enable completion triggered by <c-x><c-o>
	-- should be automated now
	-- buf_set_option('omnifunc', 'v:lua.vim.lsp.omnifunc')


	local function map(mode, lhs, rhs)
		vim.keymap.set(mode, lhs, rhs, { buffer = bufnr });
	end

	map('n', '<leader>;gD', fn(vim.lsp.buf.declaration))
	map('n', '<leader>;gd', fn(telescope.lsp_definitions))
	map('n', '<leader>;gi', fn(vim.lsp.buf.implementation))
	map('n', '<leader>;gtd', fn(vim.lsp.buf.type_definition))

	map('n', '<leader>;gr', fn(telescope.lsp_references))
	map('n', '<leader>;rn', fn(vim.lsp.buf.rename))
	map('n', '<leader>;a', fn(vim.lsp.buf.code_action))

	-- map('n','<leader>;h', vim.lsp.buf.hover)
	map('n', '<leader>;h', fn(vim.lsp.buf.signature_help))

	-- TODO: find diagnostic key maps a proper place
	map('n', '<leader>d', fn(vim.diagnostic.open_float))
	map('n', '<leader>D', fn(vim.diagnostic.setloclist))
	map('n', '[d', function() vim.diagnostic.jump({ count = -1, float = true }); end)
	map('n', '[D', function()
		vim.diagnostic.jump({ count = -1, severity = vim.diagnostic.severity.ERROR, float = true });
	end);
	map('n', ']d', function() vim.diagnostic.jump({ count = 1, float = true }); end)
	map('n', '[d', function()
		vim.diagnostic.jump({ count = -1, severity = vim.diagnostic.severity.ERROR, float = true });
	end);

	-- nmap('<leader>;s', '<cmd>lua vim.lsp.buf.document_symbol()<CR>')
	-- nmap('<leader>;S', '<cmd>lua vim.lsp.buf.workspace_symbol()<CR>')

	map('n', "<leader>F<cr>", function() vim.lsp.buf.format({ async = true }) end)
	map('n', "<leader>FF", fn(vim.lsp.buf.range_formatting))
	--

	if client.server_capabilities.document_highlight then
		local group = vim.api.nvim_create_augroup('lsp_document_highlight', {});
		vim.o.updatetime = 1000;
		vim.api.nvim_create_autocmd('CursorHold', {
			group = group,
			buffer = bufnr,
			callback = function()
				vim.lsp.buf.document_highlight()
			end,
		});
		vim.api.nvim_create_autocmd('CursorMoved', {
			group = group,
			buffer = bufnr,
			callback = function()
				vim.lsp.buf.clear_references()
			end,
		});
	end

	vim.api.nvim_set_keymap('n', '<leader>;x', '', {
		noremap = true,
		callback = function()
			for _, c in ipairs(vim.lsp.get_clients({ bufnr = bufnr })) do
				require("workspace-diagnostics").populate_workspace_diagnostics(c, 0)
			end
			vim.diagnostic.setqflist();
		end
	});
end

return {
	"neovim/nvim-lspconfig",
	dependencies = {
		"williamboman/mason-lspconfig.nvim",
		"williamboman/mason.nvim",
		'nvim-telescope/telescope.nvim',
		"https://git.sr.ht/~whynothugo/lsp_lines.nvim",
		"SmiteshP/nvim-navic",
		"artemave/workspace-diagnostics.nvim",
	},
	config = function()
		require("mason").setup()
		-- require("mason-lspconfig").setup({
		-- 	ensure_installed = {
		-- 		"lua_ls",
		-- 		-- "rust_analyzer",
		-- 		-- "wgsl_analyzer"
		-- 	},
		-- 	automatic_installation = false,
		-- })

		local lsp_config = require("lspconfig");

		-- TODO: rust

		vim.lsp.config("*", {
			capabilities = capabilities,
		})

	vim.api.nvim_create_autocmd('LspAttach', {
		callback = function(args)
			local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
			on_attach(client, args.buf)
		end,
	});

		-- npm install -g bash-language-server
		vim.lsp.enable("bashls")
		--
		-- npm install -g typescript-language-server typescript
		vim.lsp.enable("ts_ls")

		-- npm install -g pyright
		vim.lsp.enable("pyright")

		vim.lsp.config("lua_ls", {
			capabilities = capabilities,
			on_attach = on_attach,
			settings = {
				Lua = {
					runtime = {
						-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
						-- version = 'LuaJIT',
						version = 'Lua 5.2',
					},
					diagnostics = {
						-- Get the language server to recognize the `vim` global
						globals = { 'vim' },
					},
					workspace = {
						-- Make the server aware of Neovim runtime files
						library = vim.api.nvim_get_runtime_file("", true),
						checkThirdParty = false,
					},
					-- Do not send telemetry data containing a randomized but unique identifier
					telemetry = {
						enable = false,
					},
				},
			},
		});

		vim.lsp.enable("lua_ls")

		vim.lsp.enable("rust_analyzer")

		-- npm install -g dockerfile-language-server-nodejs
		vim.lsp.enable("dockerls")

		vim.lsp.enable("wgsl_analyzer")

		require("lsp_lines").setup();
		vim.diagnostic.config { virtual_text = true, virtual_lines = false };

		vim.keymap.set("", "<leader>;l", function()
			local config = vim.diagnostic.config() or {}
			if config.virtual_text then
				vim.diagnostic.config { virtual_text = false, virtual_lines = true }
			else
				vim.diagnostic.config { virtual_text = true, virtual_lines = false }
			end
		end, { desc = "Toggle lsp_lines" })
	end
}
